package com.zhang.bingmei_incomeexpenditure.service.Impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.excel.write.metadata.fill.FillWrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.zhang.bingmei_incomeexpenditure.config.ForceNewRowMergeStrategy;
import com.zhang.bingmei_incomeexpenditure.entity.Utils.IDWork;
import com.zhang.bingmei_incomeexpenditure.entity.Utils.IECommUtil;
import com.zhang.bingmei_incomeexpenditure.entity.Utils.IncomeExpenditureTestDataUtil;
import com.zhang.bingmei_incomeexpenditure.entity.Utils.SecurityPlusUtils;
import com.zhang.bingmei_incomeexpenditure.entity.build.IncomeExpenditureBuilder;
import com.zhang.bingmei_incomeexpenditure.entity.comCity.CommCity;
import com.zhang.bingmei_incomeexpenditure.entity.commom.BeanCopyUtil;
import com.zhang.bingmei_incomeexpenditure.entity.commom.DataScope;
import com.zhang.bingmei_incomeexpenditure.entity.ie.*;
import com.zhang.bingmei_incomeexpenditure.entity.ie.selectNew.SelectDoubleResult;
import com.zhang.bingmei_incomeexpenditure.entity.ie.selectNew.SelectDoubleSum;
import com.zhang.bingmei_incomeexpenditure.entity.ie.selectNew.SelectDoubleVO;
import com.zhang.bingmei_incomeexpenditure.entity.ie.updateNew.UpdateDoubleDTO;
import com.zhang.bingmei_incomeexpenditure.entity.ie.updateNew.UpdateDoubleVO;
import com.zhang.bingmei_incomeexpenditure.mapper.CommCityMapper;
import com.zhang.bingmei_incomeexpenditure.mapper.IncomeExpenditureMapper;
import com.zhang.bingmei_incomeexpenditure.service.CommCityService;
import com.zhang.bingmei_incomeexpenditure.service.IncomeExpenditureService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.time.LocalDate;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static cn.hutool.core.bean.BeanUtil.isEmpty;
import static com.zhang.comm.constant.Constant.*;
import static com.zhang.bingmei_incomeexpenditure.entity.Utils.SecurityPlusUtils.getCurrentUserId;
import static org.apache.commons.lang3.StringUtils.isBlank;


/**
 * @author zsj
 * @description 针对表【income_expenditure(病媒生物防制工作经费收支表)】的数据库操作Service实现
 * @createDate 2025-10-14 10:02:41
 */
@Service
@Slf4j
@Primary
public class IncomeExpenditureServiceImpl extends ServiceImpl<IncomeExpenditureMapper, IncomeExpenditure>
        implements IncomeExpenditureService {

    @Resource
    private IncomeExpenditureMapper incomeExpenditureMapper;
    @Resource
    private CommCityService commCityService;
    @Resource
    private CommCityMapper commCityMapper;

    /**
     * 获取数据权限
     * 当前用户ID
     * 用户等级：1系统管理员 2省级管理员 3市级管理员 4区县级管理员
     *
     * @return
     */
    @Override
    public DataScope getDataScopeByIE() {
        int minLevel = SecurityPlusUtils.getMinLevel(); //18888888888账号的 role_id：1 -> 系统管理员
        String area = SecurityPlusUtils.getArea(); //对应user的area字段：460000,460100,460106,460106004000
//        log.debug("getDataScopeByIE(): minLevel:{}  &&   area:{}",minLevel,area);
//        System.out.println("getDataScopeByIE(): minLevel:"+minLevel+"  &&   area:{}"+area);
        DataScope dataScope = new DataScope();
        // dataScope.setUserId("1");
        dataScope.setUserId("1781989837734240256");

        dataScope.setMinLevel(minLevel);
        if (StringUtils.isNotBlank(area) && minLevel != -1) {
            String[] areaList = area.split(","); // 460000,460100,460106,460106004000
            //1系统管理员和2省级管理员管辖区域都为海南省
            if (minLevel == 1 || minLevel == 2) {
                dataScope.setArea("460000"); // 由于188账号role==1 -> return 460000
                //3 市县级管理员
            } else if (minLevel == 3) {
                if (areaList.length >= 2) {
                    dataScope.setArea(areaList[0] + "," + areaList[1]);
                }
                //4区级管理员
            } else if (minLevel == 4) {
                if ((area.contains("460100") || area.contains("460200") || area.contains("460300")) && areaList.length >= 3) {
                    dataScope.setArea(areaList[0] + "," + areaList[1] + "," + areaList[2]);
                }
            }
        }

        return dataScope;
    }

    private String getArea() {
        return "460000,460100,460106,460106004000";
    }

    public DataScope getAllDistrictPrefix() {
        int minLevel = SecurityPlusUtils.getMinLevel();
        String area = SecurityPlusUtils.getArea();

        DataScope dataScope = new DataScope();
        dataScope.setUserId(getCurrentUserId());
        dataScope.setMinLevel(minLevel);

        // 空值判断
        if (isBlank(area) || minLevel == -1) {
            log.warn("用户区域信息为空或角色级别错误，返回默认数据范围");
            return dataScope;
        }

        if (StringUtils.isNotBlank(area) && minLevel != -1) {
            String[] areaList = area.split(","); // 460000,460100,460106,460106004000
            //1系统管理员和2省级管理员管辖区域都为海南省
            if (minLevel == 1 || minLevel == 2) {
                dataScope.setArea("460000"); // 由于188账号role==1 -> return 460000
                //3 市县级管理员
            } else if (minLevel == 3) {
                if (areaList.length >= 2) {
                    dataScope.setArea(areaList[0] + "," + areaList[1]);
                }
                //4区级管理员
            } else if (minLevel == 4) {
                if ((area.contains("460100") || area.contains("460200") || area.contains("460300")) && areaList.length >= 3) {
                    dataScope.setArea(areaList[0] + "," + areaList[1] + "," + areaList[2]);
                }
            }
        }

        String[] areaList = area.split(",");
        // 按角色设置区域前缀
        if (minLevel == 1 || minLevel == 2) {
            //1系统管理员 和 2省级管理员管辖区域都为海南省
            dataScope.setArea("460000");
        } else if (minLevel == 3 && areaList.length >= 2) {
            //3 市县级管理员 管辖区域是市
            dataScope.setArea(areaList[0] + "," + areaList[1]);
        } else if (minLevel == 4 && (area.contains("460100") || area.contains("460200") || area.contains("460300")) && areaList.length >= 3) {
            //区级管理员
            dataScope.setArea(areaList[0] + "," + areaList[1] + "," + areaList[2]);
        }

        log.debug("获取区域前缀完成，结果: {}", dataScope);
        return dataScope;
    }

    public List<String> getAllDistrictAfter(DataScope dataScope) {
        try {
            if (dataScope == null) {
                log.error("数据范围对象为null，返回空列表");
                return Collections.emptyList();
            }

            int userRole = dataScope.getMinLevel();
            if (userRole == -1) {
                log.error("用户角色信息错误");
                throw new RuntimeException("用户角色信息错误");
            }

            if (dataScope == null || StringUtils.isEmpty(dataScope.getArea())) {
                return new ArrayList<>();
            }

            String area = dataScope.getArea();
            if (StringUtils.isEmpty(area)) {
                log.warn("区域前缀为空，返回空列表");
                return Collections.emptyList();
            }

            List<CommCity> cityList = commCityMapper.selectDistinctLevelCodeByArea(area); //查出所有以area开头的区域
            if (CollectionUtil.isEmpty(cityList)) {
                log.warn("以{}开头编码的没有查询到数据", area);
                return Collections.emptyList();
            }

            Set<String> cityListCode = new HashSet<>();
            for (CommCity city : cityList) {
                String[] codesList = city.getLevelCode().split(",");
                // 1：系统管理员  2：省级管理员
                if ((userRole == 1 || userRole == 2) && codesList.length >= 2) {
                    // 看到的列表是全省的市辖区，依次显示海口市秀英区、海口龙华区等海南所有的市县区(不含街道，乡镇)
                    //当codesList为460000,460100时，不满足大于3，将返回codesList[1]:460100,这是不符合要求的
                    if ((codesList[1].equals("460100") || codesList[1].equals("460200") || codesList[1].equals("460300")) && codesList.length >= 3) {
                        // 海口、三亚、三沙展示区：（直接展示海口市就注释掉）
                        cityListCode.add(codesList[1] + "," + codesList[2]);
                    }
                    //当codesList为460000,460100时，不满足大于3，将返回codesList[1]:460100,这是不符合要求的
                    else if (!(codesList[1].equals("460100") || codesList[1].equals("460200") || codesList[1].equals("460300"))) {
                        cityListCode.add(codesList[1]);
                    }
                    // 3：市县级管理员
                } else if (userRole == 3 && codesList.length >= 2) {
                    // 海口、三亚、三沙（需要取区级编码）
                    if ((codesList[1].equals("460100") || codesList[1].equals("460200") || codesList[1].equals("460300"))) {
                        // 新增判断：确保数组长度≥3，才取区级编码（codesList[2]）
                        if (codesList.length >= 3) {
                            cityListCode.add(codesList[2]);
                        }
                    } else {
                        // 其他市县：添加市级编码
                        cityListCode.add(codesList[1]);
                    }
                } else if (userRole == 4 && codesList.length >= 3) {
                    // 区级管理员只返回本区编码（如460105）
                    cityListCode.add(codesList[2]);
                }
            }
            List<String> result = new ArrayList<>(cityListCode);

    /*        // 5. 返回结果
            for (Object o : cityListCode) {
                result.add(o.toString());
            }*/
            // 新增：如果结果为空，返回空集合（避免后续处理时为null）
            //        log.debug("listByDataScopeArea(): result:{}",result);
            log.debug("获取区域后缀完成，共{}条数据:\n result:{}", result.size(), result);
            return result.isEmpty() ? Collections.emptyList() : result;
        } catch (RuntimeException e) {
            if (dataScope == null) {
                log.error("数据范围对象为null，返回空列表");
                return Collections.emptyList();
            }

            int userRole = dataScope.getMinLevel();
            if (userRole == -1) {
                log.error("用户角色信息错误");
                throw new RuntimeException("用户角色信息错误");
            }

            if (StringUtils.isEmpty(dataScope.getArea())) {
                return new ArrayList<>();
            }

            String area = dataScope.getArea();
            if (StringUtils.isEmpty(area)) {
                log.warn("区域前缀为空，返回空列表");
                return Collections.emptyList();
            }

            List<CommCity> cityList = commCityMapper.selectDistinctLevelCodeByArea(area); //查出所有以area开头的区域
            if (CollectionUtil.isEmpty(cityList)) {
                log.warn("以{}开头编码的没有查询到数据", area);
                return Collections.emptyList();
            }

            Set<String> cityListCode = new HashSet<>();
            for (CommCity city : cityList) {
                //                    if (city == null || StringUtils.isEmpty(city.getLevelCode())) {
                //                        log.warn("城市对象或levelCode为空，跳过处理");
                //                        continue;
                //                    }

                String[] codesList = city.getLevelCode().split(",");
                // 1：系统管理员  2：省级管理员
                if ((userRole == 1 || userRole == 2) && codesList.length >= 2) {
                    // 看到的列表是全省的市辖区，依次显示海口市秀英区、海口龙华区等海南所有的市县区(不含街道，乡镇)
                    //当codesList为460000,460100时，不满足大于3，将返回codesList[1]:460100,这是不符合要求的
                    if ((codesList[1].equals("460100") || codesList[1].equals("460200") || codesList[1].equals("460300")) && codesList.length >= 3) {
                        // 海口、三亚、三沙展示区：（直接展示海口市就注释掉）
                        cityListCode.add(codesList[1] + "," + codesList[2]);
                    }
                    //当codesList为460000,460100时，不满足大于3，将返回codesList[1]:460100,这是不符合要求的
                    else if (!(codesList[1].equals("460100") || codesList[1].equals("460200") || codesList[1].equals("460300"))) {
                        cityListCode.add(codesList[1]);
                    }
                    // 3：市县级管理员
                } else if (userRole == 3 && codesList.length >= 2) {
                    if ((codesList[1].equals("460100") || codesList[1].equals("460200") || codesList[1].equals("460300"))) {
                        cityListCode.add(codesList[2]); // 海口、三亚、三沙显示区编码
                    } else {
                        // 其他市县：添加市级编码（codesList[1]），而非空字符串
                        cityListCode.add(codesList[1]);
                    }
                } else if (userRole == 4 && codesList.length >= 3) {
                    // 区级管理员只返回本区编码（如460105）
                    cityListCode.add(codesList[2]);
                }
            }
            List<String> result = new ArrayList<>(cityListCode);
            throw new RuntimeException("获取区域后缀时发生异常(Line:165):" + e.getMessage() + "\t\n"
                    + "currentUserId:" + SecurityPlusUtils.getCurrentUserId() + "\t\n"
                    + "currentUsername:" + SecurityPlusUtils.getCurrentUsername() + "\t\n"
                    + "maxRole:" + SecurityPlusUtils.getMinLevel() + "\t\n"
                    + "minLevel:" + SecurityPlusUtils.getMinLevel() + "\t\n"
                    + "area:" + SecurityPlusUtils.getArea() + "\t\n"
                    + "levelList:" + SecurityPlusUtils.getLevelList() + "\t\n"
                    + "result:" + result + "\t\n");
/*                            +"prefixCode:"+prefixCode+"\t\n"
                            +"districtAfter:"+districtAfter+"\t\n"
                            +"districts:"+districts);*/
        }
    }


    @Override
    public List<CodeCityNameAndYearVO> getAllDistrict(IncomeExpenditurePageDTO dto) {
        try {
            DataScope dataScope = getAllDistrictPrefix();
            String prefix = dataScope.getArea();
            if (StringUtils.isEmpty(prefix)) {
                log.error("区域前缀为空");
                throw new RuntimeException("区域前缀为空");
            }
            //这个好像有可能为空，区级和市县级的时候
            List<String> districtAfter = getAllDistrictAfter(dataScope);

            int minLevel = SecurityPlusUtils.getMinLevel();
            if (minLevel == -1) {
                log.error("用户角色信息错误");
                throw new RuntimeException("用户角色信息错误");
            }
            System.out.println("【getAllDistinct】prefix：" + prefix + "\ndistrictAfter" + districtAfter);

// 拼接出完整的城市编码
            List<String> districts = new ArrayList<>();
            for (String s : districtAfter) {
                if (StringUtils.isEmpty(s)) continue;

                // 1系统管理员和2省级管理员：拼接前缀+后缀
                if (minLevel == 1 || minLevel == 2) {
                    districts.add(prefix + "," + s);
                }
                // 3市县级管理员：仅当前缀包含海口/三亚/三沙时，拼接前缀+后缀
                else if (minLevel == 3) {
                    if (prefix.contains("460100") || prefix.contains("460200") || prefix.contains("460300")) {
                        districts.add(prefix + "," + s);
                    }
                }
                // 4区级管理员：循环内无需处理（兜底逻辑在循环外）
                else if (minLevel == 4) {
                    continue;
                }
            }

            // 循环外统一兜底：针对3、4角色，确保districts不为空（合并判断）
            if (districts.isEmpty() && StringUtils.isNotBlank(prefix)) {
                if (minLevel == 3 || minLevel == 4) {
                    districts.add(prefix);
                }
            }

            if (districts.isEmpty()) {
                log.info("没有有效的区域编码，返回空列表");
                return Collections.emptyList();
            }
            System.out.println("【getAllDisticts】districts:" + districts);

            //根据districts查询出城市
            // 查询城市信息
            QueryWrapper<CommCity> queryWrapper = new QueryWrapper<>();
            queryWrapper.in("level_code", districts)
                    .eq("deleted", 0)
                    .orderByAsc("code");

            List<CommCity> commCities = commCityMapper.selectList(queryWrapper);
            log.debug("获取城市列表完成，共查询到{}条数据", commCities == null ? 0 : commCities.size());

            if (commCities.isEmpty()) {
                log.error("未查询到城市数据");
                throw new RuntimeException("未查询到城市数据");
            }

            //将incomeExpenditures中levelCode含有“460100” “460200” “460300”的code的 district加上“海口市”“三亚市”“三沙市”
            if (CollectionUtil.isNotEmpty(commCities)) {
                for (CommCity ie : commCities) {
                    String levelCode = ie.getLevelCode();
                    String district = ie.getName();
                    // 空值防护，避免空指针
                    if (StringUtils.isEmpty(levelCode) || StringUtils.isEmpty(district)) {
                        continue;
                    }
                    // 根据levelCode包含的编码，添加对应城市前缀
                    if (levelCode.contains("460100") && !district.startsWith("海口市-")) {
                        ie.setName("海口市-" + district);
                    } else if (levelCode.contains("460200") && !district.startsWith("三亚市-")) {
                        ie.setName("三亚市-" + district);
                    } else if (levelCode.contains("460300") && !district.startsWith("三沙市-")) {
                        ie.setName("三沙市-" + district);
                    }
                }
            }

            List<CodeCityNameAndYearVO> result = new ArrayList<>();
            List<Integer> years = new ArrayList<>();
            Integer startYear = dto.getStartYear() == null ? StartYear : dto.getStartYear();
            Integer endYear = dto.getEndYear() == null ? EndYear : dto.getEndYear();
            for (int i = startYear; i <= endYear; i++) {
                years.add(i);
            }
            for (CommCity commCity : commCities) {
                result.add(new CodeCityNameAndYearVO(commCity.getCode(), commCity.getName(), years));
            }
            return result;
        } catch (RuntimeException e) {
            log.error("获取所有区域信息时发生异常", e);
            throw new RuntimeException("获取所有区域信息时发生异常");
        }
    }

    /*@Override
    public SelectDoubleResult getAllIEData(IncomeExpenditurePageDTO dto) {
        try {
            DataScope dataScope = getAllDistrictPrefix();
            String prefix = dataScope.getArea();
            if (StringUtils.isEmpty(prefix)) {
                log.error("区域前缀为空");
                throw new RuntimeException("区域前缀为空");
            }
            //这个好像有可能为空，区级和市县级的时候
            List<String> districtAfter = getAllDistrictAfter(dataScope);

            int minLevel = SecurityPlusUtils.getMinLevel();
            if (minLevel == -1) {
                log.error("用户角色信息错误");
                throw new RuntimeException("用户角色信息错误");
            }

            System.out.println("【getAllIEData】prefix："+prefix+"\n【getAllIEData】districtAfter"+districtAfter);

            //拼接出完整的城市编码
            List<String> districts = new ArrayList<>();
            String tmp = "";
            for (String s : districtAfter) {
                if (StringUtils.isEmpty(s)) continue;

                //1系统管理员和2省级管理员管辖区域都为海南省
                if ((minLevel == 1 || minLevel == 2)) {
                    districts.add(prefix + "," + s);
                    //3 市县级管理员
                } else if (minLevel == 3) {
                    if (prefix.contains("460100") || prefix.contains("460200") || prefix.contains("460300")) {
                        districts.add(prefix + "," + s);
                    } else if (!districts.contains(prefix)) { //兜底，其实可以去掉
                        districts.add(prefix);
                    }
                    //4区级管理员
                } else if ((minLevel == 4) && (!districts.contains(prefix))) {
                    districts.add(prefix);
                }
            }

            // 循环外统一兜底：针对所有角色，确保districts不为空
            if (districts.isEmpty() && StringUtils.isNotBlank(prefix)) {
                if (minLevel == 3) {
                    // 市县级：直接添加prefix（已包含省级+市级编码，如460000,460200）
                    districts.add(prefix);
                } else if (minLevel == 4) {
                    districts.add(prefix);
                }
            }

            if (districts.isEmpty()) {
                log.info("没有有效的区域编码，返回空列表");
                throw new RuntimeException("没有有效的区域编码");
            }

            //根据area字符串查询数据
            LambdaQueryWrapper<IncomeExpenditure> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.in(IncomeExpenditure::getLevelCode, districts)
                    .ge(IncomeExpenditure::getYear, dto.getStartDate() == null ? StartYear : dto.getStartYear())
                    .le(IncomeExpenditure::getYear, dto.getEndDate() == null ? EndYear : dto.getEndYear())
                    .eq(IncomeExpenditure::getDeleted, 0)
                    // 核心排序：1. 按district字段聚合（相同区域放一起） 2. 同区域内按year升序（2024在前，2025在后）
                    // 核心排序调整：1.code升序 → 2.district聚合 → 3.year升序
                    .orderByAsc(IncomeExpenditure::getCode)          // 第一步：按code字段升序（优先满足code排序）
                    .orderByAsc(IncomeExpenditure::getDistrict)      // 第二步：同code下（若有），按district聚合
                    .orderByAsc(IncomeExpenditure::getYear);         // 第三步：同district内按year升序
            List<IncomeExpenditure> incomeExpenditures = incomeExpenditureMapper.selectList(queryWrapper);
            log.debug("获取城市列表完成，共查询到{}条数据", incomeExpenditures == null ? 0 : incomeExpenditures.size());

            //将incomeExpenditures中levelCode含有“460100” “460200” “460300”的code的 district加上“海口市”“三亚市”“三沙市”
            if (CollectionUtil.isNotEmpty(incomeExpenditures)) {
                for (IncomeExpenditure ie : incomeExpenditures) {
                    String levelCode = ie.getLevelCode();
                    String district = ie.getDistrict();
                    // 空值防护，避免空指针
                    if (StringUtils.isEmpty(levelCode) || StringUtils.isEmpty(district)) {
                        continue;
                    }
                    // 根据levelCode包含的编码，添加对应城市前缀
                    if (levelCode.contains("460100") && !district.startsWith("海口市-")) {
                        ie.setDistrict("海口市-" + district);
                    } else if (levelCode.contains("460200") && !district.startsWith("三亚市-")) {
                        ie.setDistrict("三亚市-" + district);
                    } else if (levelCode.contains("460300") && !district.startsWith("三沙市-")) {
                        ie.setDistrict("三沙市-" + district);
                    }
                }
            }

            //将incomeExpenditures转换为SelectDoubleVO后返回前端
            List<UpdateDoubleVO> voList = incomeExpenditures.stream()
                    .map(this::iEToVO) // 调用转换方法
                    .collect(Collectors.toList());
            List<SelectDoubleVO> selectDoubleVOList = new ArrayList<>();
            List<SelectDoubleVO> doubleVOS = BeanCopyUtil.copyListToList(voList, SelectDoubleVO.class);

            // 6. 计算合计数据（同样转Double类型，除以PRECISION_MULTIPLIER）
            SelectDoubleSum sum = calculateDoubleTotalSum(incomeExpenditures);

            // IEResult封装返回数据
            SelectDoubleResult result = new SelectDoubleResult();
            result.setSelectDoubleResults(doubleVOS);
            result.setSelectDoubleSum(sum);

            return result;

        } catch (RuntimeException e) {
            log.error("获取所有区域信息时发生异常", e);
            throw new RuntimeException("获取所有区域信息时发生异常");
        }
    }*/

    @Override
    public SelectDoubleResult getAllIEData(IncomeExpenditurePageDTO dto) {
        try {
            DataScope dataScope = getAllDistrictPrefix();
            String prefix = dataScope.getArea();
            if (StringUtils.isEmpty(prefix)) {
                log.error("区域前缀为空");
                throw new RuntimeException("区域前缀为空");
            }
            //这个好像有可能为空，区级和市县级的时候
            List<String> districtAfter = getAllDistrictAfter(dataScope);

            int minLevel = SecurityPlusUtils.getMinLevel();
            if (minLevel == -1) {
                log.error("用户角色信息错误");
                throw new RuntimeException("用户角色信息错误");
            }

            //拼接出完整的城市编码
            List<String> districts = new ArrayList<>();
            String tmp = "";
            for (String s : districtAfter) {
                if (StringUtils.isEmpty(s) || s.equals("")) continue;

                //1系统管理员和2省级管理员管辖区域都为海南省
                if ((minLevel == 1 || minLevel == 2)) {
                    districts.add(prefix + "," + s);
                    //3 市县级管理员
                } else if (minLevel == 3) {
                    if (prefix.contains("460100") || prefix.contains("460200") || prefix.contains("460300")) {
                        districts.add(prefix + "," + s);
                    } else {
                        districts.add(prefix);
                    }
                    //4区级管理员
                } else if (minLevel == 4) {
                    if (!districts.contains(prefix)) {
                        districts.add(prefix);
                    }
                }
            }

            // 循环外统一兜底：针对所有角色，确保districts不为空
            if (districts.isEmpty() && StringUtils.isNotBlank(prefix)) {
                if (minLevel == 3) {
                    // 市县级：直接添加prefix（已包含省级+市级编码，如460000,460200）
                    districts.add(prefix);
                } else if (minLevel == 4) {
                    districts.add(prefix);
                }
            }


            if (districts.isEmpty()) {
                log.info("没有有效的区域编码，返回空列表");
                throw new RuntimeException("没有有效的区域编码");
            }

            //根据area字符串查询数据
            LambdaQueryWrapper<IncomeExpenditure> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.in(IncomeExpenditure::getLevelCode, districts)
                    .ge(IncomeExpenditure::getYear, dto.getStartDate() == null ? StartYear : dto.getStartYear())
                    .le(IncomeExpenditure::getYear, dto.getEndDate() == null ? EndYear : dto.getEndYear())
                    .eq(IncomeExpenditure::getDeleted, 0)
                    // 核心排序：1. 按district字段聚合（相同区域放一起） 2. 同区域内按year升序（2024在前，2025在后）
                    // 核心排序调整：1.code升序 → 2.district聚合 → 3.year升序
                    .orderByAsc(IncomeExpenditure::getCode)          // 第一步：按code字段升序（优先满足code排序）
                    .orderByAsc(IncomeExpenditure::getDistrict)      // 第二步：同code下（若有），按district聚合
                    .orderByAsc(IncomeExpenditure::getYear);         // 第三步：同district内按year升序
            List<IncomeExpenditure> incomeExpenditures = incomeExpenditureMapper.selectList(queryWrapper);
            log.debug("获取城市列表完成，共查询到{}条数据", incomeExpenditures == null ? 0 : incomeExpenditures.size());

            //将incomeExpenditures中levelCode含有“460100” “460200” “460300”的code的 district加上“海口市”“三亚市”“三沙市”
            if (CollectionUtil.isNotEmpty(incomeExpenditures)) {
                for (IncomeExpenditure ie : incomeExpenditures) {
                    String levelCode = ie.getLevelCode();
                    String district = ie.getDistrict();
                    // 空值防护，避免空指针
                    if (StringUtils.isEmpty(levelCode) || StringUtils.isEmpty(district)) {
                        continue;
                    }
                    // 根据levelCode包含的编码，添加对应城市前缀
                    if (levelCode.contains("460100") && !district.startsWith("海口市-")) {
                        ie.setDistrict("海口市-" + district);
                    } else if (levelCode.contains("460200") && !district.startsWith("三亚市-")) {
                        ie.setDistrict("三亚市-" + district);
                    } else if (levelCode.contains("460300") && !district.startsWith("三沙市-")) {
                        ie.setDistrict("三沙市-" + district);
                    }
                }
            }

            //将incomeExpenditures转换为SelectDoubleVO后返回前端
            List<UpdateDoubleVO> voList = incomeExpenditures.stream()
                    .map(this::iEToVO) // 调用转换方法
                    .collect(Collectors.toList());
            List<SelectDoubleVO> selectDoubleVOList = new ArrayList<>();
            List<SelectDoubleVO> doubleVOS = BeanCopyUtil.copyListToList(voList, SelectDoubleVO.class);

            // 6. 计算合计数据（同样转Double类型，除以PRECISION_MULTIPLIER）
            SelectDoubleSum sum = calculateDoubleTotalSum(incomeExpenditures);

            // IEResult封装返回数据
            SelectDoubleResult result = new SelectDoubleResult();
            result.setSelectDoubleResults(doubleVOS);
            result.setSelectDoubleSum(sum);

            return result;

        } catch (RuntimeException e) {
            log.error("获取所有区域信息时发生异常", e);
            int maxRole = SecurityPlusUtils.getMinLevel();
            List<Integer> levelList = SecurityPlusUtils.getLevelList();
            String currentUserId = getCurrentUserId();
            String currentUsername = SecurityPlusUtils.getCurrentUsername();
            String prefixCode = getAllDistrictPrefix().getArea();
            List<String> districtAfter = getAllDistrictAfter(getAllDistrictPrefix());

            //拼接出完整的城市编码
            List<String> districts = new ArrayList<>();
            String tmp = "";
            for (String s : districtAfter) {
                if (StringUtils.isEmpty(s)) continue;

                //1系统管理员和2省级管理员管辖区域都为海南省
                if ((maxRole == 1 || maxRole == 2)) {
                    districts.add(prefixCode + "," + s);
                    //3 市县级管理员
                } else if (maxRole == 3) {
                    if (s.contains("460100") || s.contains("460200") || s.contains("460300")) {
                        districts.add(prefixCode + "," + s);
                    } else if (!districts.contains(prefixCode)) { //兜底，其实可以去掉
                        districts.add(prefixCode);
                    }
                    //4区级管理员
                } else if ((maxRole == 4) && (!districts.contains(prefixCode))) { //区级：4
                    districts.add(prefixCode);
                }
            }
            // 新增：角色3专属兜底——如果districtAfter为空，直接添加prefix（当前市县级的完整编码）
            if ((maxRole == 3 || maxRole == 4) && districts.isEmpty() && StringUtils.isNotBlank(prefixCode)) {
                districts.add(prefixCode);
            }
            throw new RuntimeException("获取所有区域信息时发生异常(Line:165):" + e.getMessage() + "\t\n"
                    + "currentUserId:" + currentUserId + "\t\n"
                    + "currentUsername:" + currentUsername + "\t\n"
                    + "maxRole:" + maxRole + "\t\n"
                    + "levelList:" + levelList + "\t\n"
                    + "prefixCode:" + prefixCode + "\t\n"
                    + "districtAfter:" + districtAfter + "\t\n"
                    + "districts:" + districts);
        }
    }


    /*
     * @Param: dataScope：用户所在区域
     * return List<String>：返回用户权限下的所有市县区
     * */
    @Override
    public List<String> listByDataScopeArea(DataScope dataScope, Integer role) {
//        System.out.println("listByDataScopeArea(): dataScope:"+dataScope+"   &&   role:{}"+role);
        if (role == -1 || role == null) {
            throw new RuntimeException("用户角色信息错误");
        }
        int userRole = role;

        // 1. 校验入参
        if (dataScope == null || StringUtils.isEmpty(dataScope.getArea())) {
            return new ArrayList<>();
        }

        // 2. 获取完整area字符串（如"userId=1 minLevel=3时 -> area="460000,460100" "）
        String area = dataScope.getArea(); // 460000

//        // 4. 执行查询
//        List<CommCity> cityList = commCityService.selectList(queryWrapper);
        List<CommCity> cityList = commCityMapper.selectDistinctLevelCodeByArea(area); //查出所有以area开头的区域
        if (cityList == null || cityList.isEmpty()) {
            log.error("以{}开头编码的没有查询到数据", area);
            return new ArrayList<>();
        }

//        log.debug("cityList:{}", cityList);
//        System.out.println("listByDataScopeArea():cityList126:"+cityList);
        HashSet cityListCode = new HashSet<>();

        for (CommCity tmp : cityList) {
            String[] codesList = tmp.getLevelCode().split(",");

            if (userRole == -1) {
                log.error("用户无角色，已返回空列表...");
                return Collections.emptyList();
                //throw new RuntimeException("用户无角色");

                // 1：系统管理员  2：省级管理员
            } else if ((userRole == 1 || userRole == 2) && codesList.length >= 2) {
                // 看到的列表是全省的市辖区，依次显示海口市秀英区、海口龙华区等海南所有的市县区(不含街道，乡镇)
                //当codesList为460000,460100时，不满足大于3，将返回codesList[1]:460100,这是不符合要求的
                if ((codesList[1].equals("460100") || codesList[1].equals("460200") || codesList[1].equals("460300")) && codesList.length >= 3) {
                    // 海口、三亚、三沙展示区：（直接展示海口市就注释掉）
                    cityListCode.add(codesList[1] + "," + codesList[2]);
                }
                //当codesList为460000,460100时，不满足大于3，将返回codesList[1]:460100,这是不符合要求的
                if (codesList.length == 2 && !(codesList[1].equals("460100") || codesList[1].equals("460200") || codesList[1].equals("460300"))) {
                    cityListCode.add(codesList[1]);
                }
                // 3：市县级管理员
            } else if (userRole == 3 && codesList.length >= 3) {
                // ①海口市460100、三亚市460200、三沙市460300 看到各区的数据
                // 海南省，海口市，龙华区
                if ((codesList[1].equals("460100") || codesList[1].equals("460200") || codesList[1].equals("460300"))) {
                    cityListCode.add(codesList[2]);
                }
                // ②其他市县仅看到本市县的数据(含乡镇、街道)
                // 海南省，安定县，定城镇
                cityListCode.add(codesList[2]);

                // 4: 区级管理员
            } else if (userRole == 4 && codesList.length >= 4) {
                cityListCode.add(codesList[3]);
            }

        }
//            cityListCode.forEach(System.out::println);

        List<String> result = new ArrayList<>();

        // 5. 返回结果
        for (Object o : cityListCode) {
            result.add(o.toString());
        }
        // 新增：如果结果为空，返回空集合（避免后续处理时为null）
//        log.debug("listByDataScopeArea(): result:{}",result);
        return result.isEmpty() ? Collections.emptyList() : result;
    }

    @Override
    public IncomeExpenditureResult getCityNamesByArea(String userRoleCity,
                                                      List<String> areas, IncomeExpenditurePageDTO dto) {
        if (areas == null || areas.size() == 0) {
            log.info("getCityNamesByArea(): areas无数据,返回空集合");
            IncomeExpenditureResult emptyResult = new IncomeExpenditureResult();
            emptyResult.setIncomeExpenditureDetailVOList(Collections.emptyList());
            emptyResult.setIncomeExpenditureTotalSum(new IncomeExpenditureTotalSum());
            return emptyResult;
        }

        // 2. 拼接成 "460000,xxx" 格式的列表（核心步骤）
        List<String> levelCodeList = areas.stream()
                .map(city -> userRoleCity + "," + city)  // 每个city与userRoleCity拼接
                .collect(Collectors.toList());

        //根据area字符串查询数据
        LambdaQueryWrapper<IncomeExpenditure> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(IncomeExpenditure::getLevelCode, levelCodeList)
                .ge(IncomeExpenditure::getYear, dto.getStartDate() == null ? 2024 : dto.getStartYear())
                .le(IncomeExpenditure::getYear, dto.getEndDate() == null ? 2025 : dto.getEndYear())
                .eq(IncomeExpenditure::getDeleted, 0)
                // 核心排序：1. 按district字段聚合（相同区域放一起） 2. 同区域内按year升序（2024在前，2025在后）
                // 核心排序调整：1.code升序 → 2.district聚合 → 3.year升序
                .orderByAsc(IncomeExpenditure::getCode)          // 第一步：按code字段升序（优先满足code排序）
                .orderByAsc(IncomeExpenditure::getDistrict)      // 第二步：同code下（若有），按district聚合
                .orderByAsc(IncomeExpenditure::getYear);         // 第三步：同district内按year升序
        List<IncomeExpenditure> incomeExpenditures = incomeExpenditureMapper.selectList(queryWrapper);

        // BeanUtil.copyProperties无法直接复制列表，改为逐个对象复制
        List<IncomeExpenditureDetailVO> incomeExpenditureDetailVOS = incomeExpenditures.stream()
                .map(entity -> {
                    IncomeExpenditureDetailVO vo = new IncomeExpenditureDetailVO();
                    BeanUtil.copyProperties(entity, vo);
                    return vo;
                })
                .collect(Collectors.toList());

        //封装各列的合计数据返回前端
        IncomeExpenditureTotalSum sum = new IncomeExpenditureTotalSum();
        //设置合计值（为导出的合计数据做准备）
        sum.setCityAllocatedSum(incomeExpenditures.stream().mapToLong(IncomeExpenditure::getCityAllocated).sum());
        sum.setDistrictAllocatedSum(incomeExpenditures.stream().mapToLong(IncomeExpenditure::getDistrictAllocated).sum());
        sum.setOtherFundsSum(incomeExpenditures.stream().mapToLong(IncomeExpenditure::getOtherFunds).sum());
        sum.setTotalFundsSum(incomeExpenditures.stream().mapToLong(IncomeExpenditure::getTotalFunds).sum());
        sum.setPurchaseMaintainSum(incomeExpenditures.stream().mapToLong(IncomeExpenditure::getPurchaseMaintain).sum());
        sum.setPurchaseEquipmentSum(incomeExpenditures.stream().mapToLong(IncomeExpenditure::getPurchaseEquipment).sum());
        sum.setDiseasePreventionSum(incomeExpenditures.stream().mapToLong(IncomeExpenditure::getDiseasePrevention).sum());
        sum.setExpertTrainingSum(incomeExpenditures.stream().mapToLong(IncomeExpenditure::getExpertTraining).sum());
        sum.setThirdPartyServiceSum(incomeExpenditures.stream().mapToLong(IncomeExpenditure::getThirdPartyService).sum());
        sum.setOtherPaymentSum(incomeExpenditures.stream().mapToLong(IncomeExpenditure::getOtherPayment).sum());
        sum.setTotalPaymentSum(incomeExpenditures.stream().mapToLong(IncomeExpenditure::getTotalPayment).sum());
        sum.setCityRecoveredSum(incomeExpenditures.stream().mapToLong(IncomeExpenditure::getCityRecovered).sum());
        sum.setDistrictRecoveredSum(incomeExpenditures.stream().mapToLong(IncomeExpenditure::getDistrictRecovered).sum());

        // IEResult封装返回数据
        IncomeExpenditureResult result = new IncomeExpenditureResult();
        result.setIncomeExpenditureDetailVOList(incomeExpenditureDetailVOS);
        result.setIncomeExpenditureTotalSum(sum);

        return result;
    }

    @Override
    public SelectDoubleResult getCityNamesByAreaToDouble(String userRoleCity,
                                                         List<String> areas, IncomeExpenditurePageDTO dto) {
        if (areas == null || areas.size() == 0) {
            log.info("getCityNamesByArea(): areas无数据,返回空集合");
            SelectDoubleResult emptyResult = new SelectDoubleResult();
            emptyResult.setSelectDoubleResults(Collections.emptyList());
            emptyResult.setSelectDoubleSum(new SelectDoubleSum());
            return emptyResult;
        }

        // 2. 拼接成 "460000,xxx" 格式的列表（核心步骤）
        List<String> levelCodeList = areas.stream()
                .map(city -> userRoleCity + "," + city)  // 每个city与userRoleCity拼接
                .collect(Collectors.toList());

        //根据area字符串查询数据
        LambdaQueryWrapper<IncomeExpenditure> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(IncomeExpenditure::getLevelCode, levelCodeList)
                .ge(IncomeExpenditure::getYear, dto.getStartDate() == null ? StartYear : dto.getStartYear())
                .le(IncomeExpenditure::getYear, dto.getEndDate() == null ? EndYear : dto.getEndYear())
                .eq(IncomeExpenditure::getDeleted, 0)
                // 核心排序：1. 按district字段聚合（相同区域放一起） 2. 同区域内按year升序（2024在前，2025在后）
                // 核心排序调整：1.code升序 → 2.district聚合 → 3.year升序
                .orderByAsc(IncomeExpenditure::getCode)          // 第一步：按code字段升序（优先满足code排序）
                .orderByAsc(IncomeExpenditure::getDistrict)      // 第二步：同code下（若有），按district聚合
                .orderByAsc(IncomeExpenditure::getYear);         // 第三步：同district内按year升序
        List<IncomeExpenditure> incomeExpenditures = incomeExpenditureMapper.selectList(queryWrapper);

        //将incomeExpenditures转换为SelectDoubleVO后返回前端
        List<UpdateDoubleVO> voList = incomeExpenditures.stream()
                .map(this::iEToVO) // 调用转换方法
                .collect(Collectors.toList());
        List<SelectDoubleVO> selectDoubleVOList = new ArrayList<>();
        List<SelectDoubleVO> doubleVOS = BeanCopyUtil.copyListToList(voList, SelectDoubleVO.class);

        // 6. 计算合计数据（同样转Double类型，除以PRECISION_MULTIPLIER）
        SelectDoubleSum sum = calculateDoubleTotalSum(incomeExpenditures);

        // IEResult封装返回数据
        SelectDoubleResult result = new SelectDoubleResult();
        result.setSelectDoubleResults(doubleVOS);
        result.setSelectDoubleSum(sum);

        return result;
    }

    /**
     * 计算合计数据（Double类型）：所有金额字段求和后通过统一工具类转换
     */
    private SelectDoubleSum calculateDoubleTotalSum(List<IncomeExpenditure> entityList) {
        SelectDoubleSum sum = new SelectDoubleSum();
        if (CollectionUtil.isEmpty(entityList)) {
            return sum;
        }

        // 定义：字段提取器（从IncomeExpenditure中获取金额字段） -> 对应的sum setter方法
        List<FieldMapping> mappings = Arrays.asList(
                // 经费来源相关字段
                new FieldMapping(IncomeExpenditure::getCityAllocated, SelectDoubleSum::setCityAllocatedSum),
                new FieldMapping(IncomeExpenditure::getDistrictAllocated, SelectDoubleSum::setDistrictAllocatedSum),
                new FieldMapping(IncomeExpenditure::getOtherFunds, SelectDoubleSum::setOtherFundsSum),
                new FieldMapping(IncomeExpenditure::getTotalFunds, SelectDoubleSum::setTotalFundsSum),
                // 已支付相关字段
                new FieldMapping(IncomeExpenditure::getPurchaseMaintain, SelectDoubleSum::setPurchaseMaintainSum),
                new FieldMapping(IncomeExpenditure::getPurchaseEquipment, SelectDoubleSum::setPurchaseEquipmentSum),
                new FieldMapping(IncomeExpenditure::getDiseasePrevention, SelectDoubleSum::setDiseasePreventionSum),
                new FieldMapping(IncomeExpenditure::getExpertTraining, SelectDoubleSum::setExpertTrainingSum),
                new FieldMapping(IncomeExpenditure::getThirdPartyService, SelectDoubleSum::setThirdPartyServiceSum),
                new FieldMapping(IncomeExpenditure::getOtherPayment, SelectDoubleSum::setOtherPaymentSum),
                new FieldMapping(IncomeExpenditure::getTotalPayment, SelectDoubleSum::setTotalPaymentSum),
                // 收回资金相关字段
                new FieldMapping(IncomeExpenditure::getCityRecovered, SelectDoubleSum::setCityRecoveredSum),
                new FieldMapping(IncomeExpenditure::getDistrictRecovered, SelectDoubleSum::setDistrictRecoveredSum)
        );

        // 循环处理所有字段：计算合计 -> 转换 -> 赋值
        mappings.forEach(mapping -> {
            // 1. 计算当前字段的Long类型合计（自动处理null为0）
            long fieldSum = entityList.stream()
                    .mapToLong(entity -> {
                        Long value = mapping.fieldExtractor.apply(entity);
                        return value == null ? 0 : value;
                    })
                    .sum();
            // 2. 转换为Double并通过setter赋值给sum对象
            mapping.sumSetter.accept(sum, IECommUtil.longToDouble(fieldSum));
        });

        return sum;
    }

    // 内部静态类：封装字段提取器和sum的setter方法
    private static class FieldMapping {
        // 从IncomeExpenditure中提取金额字段的函数
        Function<IncomeExpenditure, Long> fieldExtractor;
        // 给SelectDoubleSum设置转换后值的函数
        BiConsumer<SelectDoubleSum, Double> sumSetter;

        public FieldMapping(Function<IncomeExpenditure, Long> fieldExtractor,
                            BiConsumer<SelectDoubleSum, Double> sumSetter) {
            this.fieldExtractor = fieldExtractor;
            this.sumSetter = sumSetter;
        }
    }


    /**
     * 自定义函数式接口求和
     *
     * @param entityList 任意实体的列表
     * @param filed      字段提取函数（如 IncomeExpenditure::getCityAllocated）
     * @param <T>        实体类型
     * @return 该列的全局合计（Long类型）
     */
    public static <T> Long sumByFunction(List<T> entityList, Function<T, Long> filed) {
        if (CollectionUtil.isEmpty(entityList)) {
            return 0L;
        }
        // 流式遍历求和（空值按0计算）
        return entityList.stream()
                .map(filed)
                .mapToLong(value -> value != null ? value : 0L)
                .sum();
    }

    /**
     * 批量修改收支数据（支持多条不同ID、不同字段的修改）
     *
     * @param updateList 批量修改参数（包含多条待修改数据的ID和字段）
     * @return 修改后的明细VO列表（便于前端刷新展示）
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<IncomeExpenditureDetailVO> batchUpdateIncomeExpenditureData(List<UpdateIncomeExpenditureDTO> updateList) {
        // 1. 基础参数校验
        if (CollectionUtil.isEmpty(updateList)) {
            throw new RuntimeException("待修改数据列表不能为空");
        }

        // 2. 权限校验：仅市县级（3）、区级（4）管理员可修改
        List<Integer> levelList = SecurityPlusUtils.getLevelList();
        // 判断levelList既不包含3，也不包含4
        if (levelList == null || (!levelList.contains(3) && !levelList.contains(4))) {
            // 执行逻辑：levelList为空 或 不包含3且不包含4时的处理
            log.error("当前用户无修改权限角色");
            throw new RuntimeException("当前用户无修改权限角色");
        }

        // 3. 提取所有待修改数据的ID，校验非空
        List<String> updateIds = updateList.stream()
                .map(UpdateIncomeExpenditureDTO::getId)
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.toList());

        if (CollectionUtil.isEmpty(updateIds)) {
            throw new RuntimeException("所有修改项的ID不能为空");
        }

        // 4. 查询原始数据（用于权限校验和字段覆盖）
        LambdaQueryWrapper<IncomeExpenditure> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(IncomeExpenditure::getId, updateIds)
                .eq(IncomeExpenditure::getDeleted, 0);
        List<IncomeExpenditure> originalEntities = this.list(queryWrapper);

        // 4.1 校验数据存在性
        if (originalEntities.size() != updateIds.size()) {
            List<String> existIds = originalEntities.stream()
                    .map(IncomeExpenditure::getId)
                    .collect(Collectors.toList());
            List<String> invalidIds = updateIds.stream()
                    .filter(id -> !existIds.contains(id))
                    .collect(Collectors.toList());
            throw new RuntimeException("ID为" + String.join(",", invalidIds) + "的数据不存在或已删除");
        }

        // 5.1 构建原始数据ID映射，便于匹配
        Map<String, IncomeExpenditure> originalMap = originalEntities.stream()
                .collect(Collectors.toMap(IncomeExpenditure::getId, entity -> entity));

        // 6. 构建修改后的实体列表（只更新DTO中非null的字段）
        List<IncomeExpenditure> updateEntities = updateList.stream()
                .map(dto -> {
                    IncomeExpenditure original = originalMap.get(dto.getId());
                    if (original == null) return null;

                    // 复制原始实体（避免直接修改查询结果）
                    IncomeExpenditure updateEntity = new IncomeExpenditure();
                    BeanUtil.copyProperties(original, updateEntity);

                    // 覆盖DTO中非null的字段（同步修改为最新DTO字段）
                    if (StringUtils.isNotBlank(dto.getDistrict())) {
                        updateEntity.setDistrict(dto.getDistrict());
                    }
                    if (StringUtils.isNotBlank(dto.getYear())) {
                        updateEntity.setYear(Integer.valueOf(dto.getYear()));
                    }
                    // 经费来源字段（DTO新字段 -> 实体字段）
                    if (dto.getCityAllocated() != null) {
                        updateEntity.setCityAllocated(dto.getCityAllocated()); // DTO新字段 cityAllocated
                    }
                    if (dto.getDistrictAllocated() != null) {
                        updateEntity.setDistrictAllocated(dto.getDistrictAllocated()); // DTO新字段 districtAllocated
                    }
                    if (dto.getOtherFunds() != null) {
                        updateEntity.setOtherFunds(dto.getOtherFunds()); // 字段名未变
                    }
                    if (dto.getTotalFunds() != null) {
                        updateEntity.setTotalFunds(dto.getTotalFunds()); // DTO新字段 totalFunds（建议后端自动计算）
                    }
                    // 已支付字段（DTO新字段 -> 实体字段）
                    if (dto.getPurchaseMaintain() != null) {
                        updateEntity.setPurchaseMaintain(dto.getPurchaseMaintain()); // DTO新字段 purchaseMaintain
                    }
                    if (dto.getPurchaseEquipment() != null) {
                        updateEntity.setPurchaseEquipment(dto.getPurchaseEquipment()); // DTO新字段 purchaseEquipment
                    }
                    if (dto.getDiseasePrevention() != null) {
                        updateEntity.setDiseasePrevention(dto.getDiseasePrevention()); // DTO新字段 diseasePrevention
                    }
                    if (dto.getExpertTraining() != null) {
                        updateEntity.setExpertTraining(dto.getExpertTraining()); // DTO新字段 expertTraining
                    }
                    if (dto.getThirdPartyService() != null) {
                        updateEntity.setThirdPartyService(dto.getThirdPartyService()); // DTO新字段 thirdPartyService
                    }
                    if (dto.getOtherPayment() != null) {
                        updateEntity.setOtherPayment(dto.getOtherPayment()); // DTO新字段 otherPayment
                    }
                    if (dto.getTotalPayment() != null) {
                        updateEntity.setTotalPayment(dto.getTotalPayment()); // DTO新字段 totalPayment（建议后端自动计算）
                    }
                    // 被收回资金字段（DTO新字段 -> 实体字段）
                    if (dto.getCityRecovered() != null) {
                        updateEntity.setCityRecovered(dto.getCityRecovered()); // DTO新字段 cityRecovered
                    }
                    if (dto.getDistrictRecovered() != null) {
                        updateEntity.setDistrictRecovered(dto.getDistrictRecovered()); // DTO新字段 districtRecovered
                    }

                    // 自动计算合计字段（基于DTO修改的基础字段，优先级高于DTO传入的合计值）
                    boolean needCalcFundsTotal = dto.getCityAllocated() != null || dto.getDistrictAllocated() != null || dto.getOtherFunds() != null;
                    boolean needCalcPaymentTotal = dto.getPurchaseMaintain() != null || dto.getPurchaseEquipment() != null
                            || dto.getDiseasePrevention() != null || dto.getExpertTraining() != null
                            || dto.getThirdPartyService() != null || dto.getOtherPayment() != null;

                    if (needCalcFundsTotal) {
                        updateEntity.setTotalFunds(calcFundsSourceTotal(updateEntity)); // 自动计算经费来源合计
                    }
                    if (needCalcPaymentTotal) {
                        updateEntity.setTotalPayment(calcPaymentTotal(updateEntity)); // 自动计算已支付合计
                    }

                    // 补充更新信息
                    updateEntity.setUpdateBy(SecurityPlusUtils.getCurrentUsername());
                    updateEntity.setUpdateTime(new Date());

                    return updateEntity;
                })
                .filter(Objects::nonNull) // 过滤空值
                .collect(Collectors.toList());

        // 7. 执行批量更新
        this.updateBatchById(updateEntities);

        // 8. 查询更新后的最新数据并返回VO
        List<IncomeExpenditure> updatedEntities = this.listByIds(updateIds);
        List<IncomeExpenditureDetailVO> incomeExpenditureDetailVOS = BeanCopyUtil.copyListToList(updatedEntities, IncomeExpenditureDetailVO.class);
        return incomeExpenditureDetailVOS;
    }

    /**
     * 计算经费来源合计（同步修改为实体新字段）
     */
    private Long calcFundsSourceTotal(IncomeExpenditure entity) {
        long city = entity.getCityAllocated() == null ? 0 : entity.getCityAllocated();
        long district = entity.getDistrictAllocated() == null ? 0 : entity.getDistrictAllocated();
        long other = entity.getOtherFunds() == null ? 0 : entity.getOtherFunds();
        return city + district + other;
    }

    /**
     * 计算已支付合计（同步修改为实体新字段）
     */
    private Long calcPaymentTotal(IncomeExpenditure entity) {
        long facility = entity.getPurchaseMaintain() == null ? 0 : entity.getPurchaseMaintain();
        long equipment = entity.getPurchaseEquipment() == null ? 0 : entity.getPurchaseEquipment();
        long publicity = entity.getDiseasePrevention() == null ? 0 : entity.getDiseasePrevention();
        long expert = entity.getExpertTraining() == null ? 0 : entity.getExpertTraining();
        long thirdParty = entity.getThirdPartyService() == null ? 0 : entity.getThirdPartyService();
        long other = entity.getOtherPayment() == null ? 0 : entity.getOtherPayment();
        return facility + equipment + publicity + expert + thirdParty + other;
    }

    @Override
    public void exportIncomeExpenditure(IncomeExpenditurePageDTO dto, HttpServletResponse response) throws Exception {
        IncomeExpenditureResult2 exportResultData = IncomeExpenditureTestDataUtil.getExportData();
        //一般导出、下载等接口，有关于文件的保存的，返回的都是void，也就是不返回数据，只通过reponse去返回响应数据（文件流）
        InputStream templateStream = getClass().getClassLoader().getResourceAsStream("temp/IE-template.xlsx");
        if (templateStream == null) {
            //这里如果获取不到的话，可以使用绝对路径去读取
            templateStream = this.getClass().getClassLoader().getResourceAsStream("E:\\file\\demo\\src\\main\\resources\\temp\\IE-template.xlsx");
        }
        response.setContentType("application/vnd.ms-excel");
        response.setHeader("Content-Disposition", "attachment; filename=example.xlsx");
        ServletOutputStream outputStream = response.getOutputStream();
        // ---------- 1. 准备 Map（非列表部分） ----------
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("startDate", dto.getStartDate());
        dataMap.put("endDate", dto.getEndDate());
        ObjectMapper mapper = new ObjectMapper();
        Map<String, Object> sumMap = mapper.convertValue(exportResultData.getIncomeExpenditureTotalSum(), Map.class);
        dataMap.putAll(sumMap);
        // ---------- 2. 准备 List（列表部分） ----------
        List<IncomeExpenditureExportVO> detailList = exportResultData.getIncomeExpenditureExportVOList();
        // ---------- 3. 写出 ----------
        //创建自定义合并规则
        ForceNewRowMergeStrategy mergeStrategy = new ForceNewRowMergeStrategy(0, new int[]{0});
        ExcelWriter excelWriter = EasyExcel.write(outputStream)
                .withTemplate(templateStream)
                .excelType(ExcelTypeEnum.XLSX)
                .build();
        //这一行必须加：否则会遇见占位符覆盖的现象，这个比较恶心，会导致最后一行合计无法正常显示
        FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
        WriteSheet writeSheet = EasyExcel.writerSheet()
                //应用合并规则
                .registerWriteHandler(mergeStrategy)
                .build();
        // 关键：分两次 fill
        // 合计、日期等普通占位符
        excelWriter.fill(dataMap, writeSheet);
        // 列表占位符  如果是多个的话，就会用到这个FillWrapper，我这是为了做演示，就给你加了一下，对应的模板也对应加了一下
        excelWriter.fill(new FillWrapper("data", detailList), fillConfig, writeSheet);
        excelWriter.finish();
    }

    @Override
    public IncomeExpenditureResult getIncomeExpenditureData(IncomeExpenditurePageDTO dto) {
        //通过getDataScope()获取当前用户所在城市，return所在城市 DataScope，从460000开始
        //通过listByDataScopeArea()获取所在城市下的市县区街道，return citys的编码集合
        //通过getCityNamesByArea()，将通过getDataScope()获取的所在城市 与 listByDataScopeArea()的市县区街道编码合并（即完整编码，对应level_code）
        //根据合并后的level_code在表中查询数据（在getCityNamesByArea()实现）
        DataScope scope = this.getDataScopeByIE();  //传入的是18888888888账号，user_id:1 分析结果：460000
        Integer roleLevel = SecurityPlusUtils.getMinLevel(); // role_level:1
        List<String> citys = this.listByDataScopeArea(scope, roleLevel);

        return this.getCityNamesByArea(scope.getArea(), citys, dto);
    }

    public SelectDoubleResult getIncomeExpenditureDoubleData(IncomeExpenditurePageDTO dto) {
        //通过getDataScope()获取当前用户所在城市，return所在城市 DataScope，从460000开始
        //通过listByDataScopeArea()获取所在城市下的市县区街道，return citys的编码集合
        //通过getCityNamesByArea()，将通过getDataScope()获取的所在城市 与 listByDataScopeArea()的市县区街道编码合并（即完整编码，对应level_code）
        //根据合并后的level_code在表中查询数据（在getCityNamesByArea()实现）
        DataScope scope = this.getDataScopeByIE();  //传入的是18888888888账号，user_id:1 分析结果：460000
        Integer roleLevel = SecurityPlusUtils.getMinLevel(); // role_level:1
        List<String> citys = this.listByDataScopeArea(scope, roleLevel);

        return this.getCityNamesByAreaToDouble(scope.getArea(), citys, dto);
    }

    /**
     * 批量修改收支数据,转long值
     *
     * @param updateList 批量修改参数（包含多条待修改数据的ID和字段）
     * @return 修改后的明细VO列表（便于前端刷新展示）
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<UpdateDoubleVO> batchUpdateIncomeExpenditureDataLong(List<UpdateDoubleDTO> updateList) {
        // 1. 基础参数校验
        if (CollectionUtil.isEmpty(updateList)) {
            throw new RuntimeException("待修改数据列表不能为空");
        }
/*
        // 2. 权限校验：仅市县级（3）、区级（4）管理员可修改
        Boolean isUpdate = IECommUtil.isUpdateButton(); //true可以修改
        if (!isUpdate) {
            throw new RuntimeException("当前用户无法修改收支数据的角色权限aaa,当前用户角色:" + SecurityPlusUtils.getCurrentUserRoleLevel());
        }*/

        // 3. 提取所有待修改数据的ID，校验非空
        List<String> updateIds = updateList.stream()
                .map(UpdateDoubleDTO::getId)
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.toList());

        if (CollectionUtil.isEmpty(updateIds)) {
            throw new RuntimeException("所有修改项的ID不能为空");
        }

        // 4. 查询原始数据（用于权限校验和字段覆盖）
        LambdaQueryWrapper<IncomeExpenditure> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(IncomeExpenditure::getId, updateIds)
                .eq(IncomeExpenditure::getDeleted, 0);
        List<IncomeExpenditure> originalEntities = this.list(queryWrapper); //用id去数据库查，查出来的是转Long的数据（乘10000000）

        // 4.1 校验数据存在性
        if (originalEntities.size() != updateIds.size()) {
            List<String> existIds = originalEntities.stream()
                    .map(IncomeExpenditure::getId)
                    .collect(Collectors.toList());
            List<String> invalidIds = updateIds.stream()
                    .filter(id -> !existIds.contains(id))
                    .collect(Collectors.toList());
            throw new RuntimeException("ID为" + String.join(",", invalidIds) + "的数据不存在或已删除");
        }

        // 5.1 构建原始数据ID映射，便于匹配
        Map<String, IncomeExpenditure> originalMap = originalEntities.stream()
                .collect(Collectors.toMap(IncomeExpenditure::getId, entity -> entity));

        // 6. 构建修改后的实体列表（只更新DTO中非null的字段，将前端的数据转换和封装）
        List<IncomeExpenditure> updateEntities = updateList.stream()
                .map(dto -> {
                    IncomeExpenditure original = originalMap.get(dto.getId());
                    if (original == null) return null;

                    // 复制原始实体（避免直接修改查询结果）
                    IncomeExpenditure updateEntity = new IncomeExpenditure();
                    BeanUtil.copyProperties(original, updateEntity);

                    System.out.println("581行：updateEntity:" + updateEntity);

                    // 覆盖DTO中非null的字段（同步修改为最新DTO字段）
                    // 年份和市辖区应该不支持修改吧？ TODO：待确认
                    if (StringUtils.isNotBlank(dto.getDistrict())) {
                        updateEntity.setDistrict(dto.getDistrict());
                    }
                    if (StringUtils.isNotBlank(dto.getYear())) {
                        updateEntity.setYear(Integer.valueOf(dto.getYear()));
                    }

                    //将前端的Double值转成数据库需要的Long值
                    IncomeExpenditure temp = moneyToDB(dto);
                    BeanCopyUtil.copyPropertiesIgnoreNull(temp, updateEntity);

                    // 补充更新信息
                    updateEntity.setUpdateBy(SecurityPlusUtils.getCurrentUsername());
                    updateEntity.setUpdateTime(new Date());

                    return updateEntity;
                })
                .filter(Objects::nonNull) // 过滤空值
                .collect(Collectors.toList());

        // 7. 执行批量更新
        this.updateBatchById(updateEntities);

        // 8. 查询更新后的最新数据并返回VO
        List<IncomeExpenditure> updatedEntities = this.listByIds(updateIds);
        //List<IncomeExpenditureDetailVO> incomeExpenditureDetailVOS = BeanCopyUtil.copyListToList(updatedEntities, IncomeExpenditureDetailVO.class);
        List<UpdateDoubleVO> voList = updatedEntities.stream()
                .map(this::iEToVO) // 调用转换方法
                .collect(Collectors.toList());
        return voList;
    }
    /**
     * 批量修改/新增收支数据（增强版：含权限控制、一致性校验和唯一性校验）
     */
    /*@Transactional(rollbackFor = Exception.class) // 事务注解：确保异常时全量回滚
    @Override
    public List<UpdateDoubleVO> batchUpdateIncomeExpenditureDataLong2(List<UpdateDoubleDTO> updateList) {
        // 1. 基础参数校验
        if (CollectionUtil.isEmpty(updateList)) {
            throw new RuntimeException("待处理数据列表不能为空");
        }

        // 2. 按ID分组：拆分“有ID（修改）”和“无ID（新增）”的数据
        Map<Boolean, List<UpdateDoubleDTO>> groupedData = updateList.stream()
                .collect(Collectors.partitioningBy(dto -> StringUtils.isNotBlank(dto.getId())));
        List<UpdateDoubleDTO> updateDTOList = groupedData.get(Boolean.TRUE); // 修改
        List<UpdateDoubleDTO> insertDTOList = groupedData.get(Boolean.FALSE); // 新增

        // 3. 处理修改逻辑（新增校验：禁止修改年份和市辖区，且校验code和district一致性）
        List<IncomeExpenditure> updateEntities = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(updateDTOList)) {
            // 3.1 提取所有待修改数据的ID
            List<String> updateIds = updateDTOList.stream()
                    .map(UpdateDoubleDTO::getId)
                    .collect(Collectors.toList());

            // 3.2 查询原始数据（用于校验和覆盖）
            LambdaQueryWrapper<IncomeExpenditure> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.in(IncomeExpenditure::getId, updateIds)
                    .eq(IncomeExpenditure::getDeleted, 0);
            List<IncomeExpenditure> originalEntities = this.list(queryWrapper);
            if (originalEntities.size() != updateIds.size()) {
                List<String> existIds = originalEntities.stream().map(IncomeExpenditure::getId).collect(Collectors.toList());
                List<String> invalidIds = updateIds.stream().filter(id -> !existIds.contains(id)).collect(Collectors.toList());
                throw new RuntimeException("ID为" + String.join(",", invalidIds) + "的数据不存在或已删除");
            }

            // 3.3 构建原始数据ID映射
            Map<String, IncomeExpenditure> originalMap = originalEntities.stream()
                    .collect(Collectors.toMap(IncomeExpenditure::getId, entity -> entity));

            // 3.4 校验：禁止修改年份和市辖区，且code/district必须与原始数据一致
            for (UpdateDoubleDTO dto : updateDTOList) {
                IncomeExpenditure original = originalMap.get(dto.getId());
                // 校验1：禁止修改年份（若前端传了与原始不同的年份）
                if (StringUtils.isNotBlank(dto.getYear())) {
                    Integer newYear = Integer.valueOf(dto.getYear());
                    if (!newYear.equals(original.getYear())) {
                        throw new RuntimeException("ID为" + dto.getId() + "的数据禁止修改年份（原始年份：" + original.getYear() + "）");
                    }
                }
                // 校验2：禁止修改市辖区（若前端传了与原始不同的district）
                if (StringUtils.isNotBlank(dto.getDistrict()) && !dto.getDistrict().equals(original.getDistrict())) {
                    throw new RuntimeException("ID为" + dto.getId() + "的数据禁止修改市辖区（原始区域：" + original.getDistrict() + "）");
                }
                // 校验3：code必须与原始数据一致（前端传了code但与原始不同）
                if (StringUtils.isNotBlank(dto.getCode()) && !dto.getCode().equals(original.getCode())) {
                    throw new RuntimeException("ID为" + dto.getId() + "的数据code不匹配（原始code：" + original.getCode() + "）");
                }
            }

            // 3.5 构建修改实体（仅更新允许修改的字段）
            updateEntities = updateDTOList.stream()
                    .map(dto -> {
                        IncomeExpenditure original = originalMap.get(dto.getId());
                        IncomeExpenditure updateEntity = new IncomeExpenditure();
                        BeanUtil.copyProperties(original, updateEntity);

                        // 仅允许修改金额字段（禁止修改年份、district、code）
                        IncomeExpenditure temp = moneyToDB(dto);
                        BeanCopyUtil.copyPropertiesIgnoreNull(temp, updateEntity);

                        // 补充更新信息
                        updateEntity.setUpdateBy(SecurityPlusUtils.getCurrentUsername());
                        updateEntity.setUpdateTime(new Date());
                        return updateEntity;
                    })
                    .collect(Collectors.toList());

            // 3.6 执行批量修改
            this.updateBatchById(updateEntities);
        }

        // 4. 处理新增逻辑（完整校验+赋值）
        List<IncomeExpenditure> insertEntities = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(insertDTOList)) {
            // 4.1 基础参数校验（非空+格式）
            int currentYear = LocalDate.now().getYear(); // 获取当前年份（动态适配）
            for (UpdateDoubleDTO dto : insertDTOList) {
                // 校验1：必填字段非空
                if (StringUtils.isBlank(dto.getCode())) {
                    throw new RuntimeException("新增数据的code不能为空");
                }
                if (StringUtils.isBlank(dto.getDistrict())) {
                    throw new RuntimeException("新增数据的district不能为空");
                }
                if (StringUtils.isBlank(dto.getYear())) {
                    throw new RuntimeException("新增数据的year不能为空");
                }

                // 校验2：年份格式及合法性（不能大于当前年）
                try {
                    int inputYear = Integer.parseInt(dto.getYear());
                    if (inputYear > currentYear) {
                        throw new RuntimeException("新增失败：年份" + inputYear + "不能大于当前年" + currentYear);
                    }
                } catch (NumberFormatException e) {
                    throw new RuntimeException("新增失败：年份" + dto.getYear() + "格式错误，必须为数字（如2024）");
                }
            }

            // 4.2 唯一性校验（code+district+year不能已存在）
            for (UpdateDoubleDTO dto : insertDTOList) {
                LambdaQueryWrapper<IncomeExpenditure> existQuery = new LambdaQueryWrapper<>();
                existQuery.eq(IncomeExpenditure::getCode, dto.getCode())
                        .eq(IncomeExpenditure::getDistrict, dto.getDistrict())
                        .eq(IncomeExpenditure::getYear, Integer.parseInt(dto.getYear()))
                        .eq(IncomeExpenditure::getDeleted, 0); // 排除已删除数据

                long count = this.count(existQuery);
                if (count > 0) {
                    throw new RuntimeException(
                            "新增失败：code=" + dto.getCode() +
                                    "、district=" + dto.getDistrict() +
                                    "、year=" + dto.getYear() + "的数据已存在"
                    );
                }
            }

            // 4.3 关联查询comm_city表并赋值
            insertEntities = insertDTOList.stream()
                    .map(dto -> {
                        // 查询comm_city表（只获取需要的4个字段）
                        LambdaQueryWrapper<CommCity> cityQueryWrapper = new LambdaQueryWrapper<>();
                        cityQueryWrapper.eq(CommCity::getCode, dto.getCode())
                                .eq(CommCity::getName, dto.getDistrict())
                                .eq(CommCity::getDeleted, 0)
                                .select(
                                        CommCity::getCode,
                                        CommCity::getLevelCode,
                                        CommCity::getMergerName,
                                        CommCity::getParentCode
                                );
                        CommCity commCity = commCityMapper.selectOne(cityQueryWrapper);

                        // 校验城市数据是否存在
                        if (commCity == null) {
                            throw new RuntimeException(
                                    "新增失败：未找到code=" + dto.getCode() +
                                            "、name=" + dto.getDistrict() +"的城市信息"
                            );
                        }

                        // 4.4 构建新增实体
                        IncomeExpenditure insertEntity = new IncomeExpenditure();
                        // 设置ID（使用工具类生成）
                        insertEntity.setId(IDWork.getSequenceId2String());
                        // 基础字段赋值
                        insertEntity.setCode(commCity.getCode());
                        insertEntity.setDistrict(dto.getDistrict());
                        insertEntity.setYear(Integer.parseInt(dto.getYear()));
                        // 从comm_city查询的字段
                        insertEntity.setLevelCode(commCity.getLevelCode());
                        insertEntity.setMergerName(commCity.getMergerName());
                        insertEntity.setParentCode(commCity.getParentCode());
                        // 默认值
                        insertEntity.setDeleted(false);
                        insertEntity.setCreateBy(SecurityPlusUtils.getCurrentUsername());
                        insertEntity.setCreateTime(new Date());
                        insertEntity.setUpdateBy(SecurityPlusUtils.getCurrentUsername());
                        insertEntity.setUpdateTime(new Date());

                        // 4.5 金额转换（前端Double → 数据库Long）
                        IncomeExpenditure temp = moneyToDB(dto);
                        BeanCopyUtil.copyPropertiesIgnoreNull(temp, insertEntity);

                        // 4.6 自动计算合计字段
                        insertEntity.setTotalFunds(calcFundsSourceTotal(insertEntity));
                        insertEntity.setTotalPayment(calcPaymentTotal(insertEntity));

                        return insertEntity;
                    })
                    .collect(Collectors.toList());

            // 4.7 执行批量新增
            if (CollectionUtil.isNotEmpty(insertEntities)) {
                this.saveBatch(insertEntities);
            }
        }

        // 5. 返回处理结果
        List<String> allIds = new ArrayList<>();
        allIds.addAll(updateEntities.stream().map(IncomeExpenditure::getId).collect(Collectors.toList()));
        allIds.addAll(insertEntities.stream().map(IncomeExpenditure::getId).collect(Collectors.toList()));
        List<IncomeExpenditure> resultEntities = this.listByIds(allIds);
        return resultEntities.stream().map(this::iEToVO).collect(Collectors.toList());
    }
*/

    private IncomeExpenditure moneyToDB(UpdateDoubleDTO dto) {
        // 复制原始实体（避免直接改查询结果）
        IncomeExpenditure updateEntity = new IncomeExpenditure();
        // 经费来源字段： 市级下拨经费
        if (dto.getCityAllocated() != null) {  //前端传过来的是带有小数的
            updateEntity.setCityAllocated(IECommUtil.transferToLong(dto.getCityAllocated()));
        }
        if (dto.getDistrictAllocated() != null) {
            updateEntity.setDistrictAllocated(IECommUtil.transferToLong(dto.getDistrictAllocated())); // DTO新字段 districtAllocated
        }
        if (dto.getOtherFunds() != null) {
            updateEntity.setOtherFunds(IECommUtil.transferToLong(dto.getOtherFunds())); // 字段名未变
        }
        if (dto.getTotalFunds() != null) {
            updateEntity.setTotalFunds(IECommUtil.transferToLong(dto.getTotalFunds())); // DTO新字段 totalFunds（建议后端自动计算）
        }
        // 已支付字段（DTO新字段 -> 实体字段）
        if (dto.getPurchaseMaintain() != null) {
            updateEntity.setPurchaseMaintain(IECommUtil.transferToLong(dto.getPurchaseMaintain())); // DTO新字段 purchaseMaintain
        }
        if (dto.getPurchaseEquipment() != null) {
            updateEntity.setPurchaseEquipment(IECommUtil.transferToLong(dto.getPurchaseEquipment())); // DTO新字段 purchaseEquipment
        }
        if (dto.getDiseasePrevention() != null) {
            updateEntity.setDiseasePrevention(IECommUtil.transferToLong(dto.getDiseasePrevention())); // DTO新字段 diseasePrevention
        }
        if (dto.getExpertTraining() != null) {
            updateEntity.setExpertTraining(IECommUtil.transferToLong(dto.getExpertTraining())); // DTO新字段 expertTraining
        }
        if (dto.getThirdPartyService() != null) {
            updateEntity.setThirdPartyService(IECommUtil.transferToLong(dto.getThirdPartyService())); // DTO新字段 thirdPartyService
        }
        if (dto.getOtherPayment() != null) {
            updateEntity.setOtherPayment(IECommUtil.transferToLong(dto.getOtherPayment())); // DTO新字段 otherPayment
        }
        if (dto.getTotalPayment() != null) {
            updateEntity.setTotalPayment(IECommUtil.transferToLong(dto.getTotalPayment())); // DTO新字段 totalPayment（建议后端自动计算）
        }
        // 被收回资金字段（DTO新字段 -> 实体字段）
        if (dto.getCityRecovered() != null) {
            updateEntity.setCityRecovered(IECommUtil.transferToLong(dto.getCityRecovered())); // DTO新字段 cityRecovered
        }
        if (dto.getDistrictRecovered() != null) {
            updateEntity.setDistrictRecovered(IECommUtil.transferToLong(dto.getDistrictRecovered())); // DTO新字段 districtRecovered
        }

        // 自动计算合计字段（基于DTO修改的基础字段，优先级高于DTO传入的合计值）
        boolean needCalcFundsTotal = dto.getCityAllocated() != null || dto.getDistrictAllocated() != null || dto.getOtherFunds() != null;
        boolean needCalcPaymentTotal = dto.getPurchaseMaintain() != null || dto.getPurchaseEquipment() != null
                || dto.getDiseasePrevention() != null || dto.getExpertTraining() != null
                || dto.getThirdPartyService() != null || dto.getOtherPayment() != null;

        if (needCalcFundsTotal) {
            Long totalFunds = calcFundsSourceTotal(updateEntity);
            if (totalFunds != updateEntity.getTotalFunds()) {
                log.error("经费来源合计不一致-前端传入的数值：{}，后端计算的数据：{}", updateEntity.getTotalFunds(), totalFunds);
            }
            updateEntity.setTotalFunds(totalFunds); // 自动计算经费来源合计,后端为准
        }
        if (needCalcPaymentTotal) {
            Long totalPayment = calcPaymentTotal(updateEntity);
            if (totalPayment != updateEntity.getTotalPayment()) {
                log.error("以实际完成支付合计不一致-前端传入的数值：{}，后端计算的数据：{}", updateEntity.getTotalPayment(), totalPayment);
            }
            updateEntity.setTotalPayment(calcPaymentTotal(updateEntity)); // 自动计算已支付合计
        }
        return updateEntity;
    }

    /**
     * 批量修改/新增收支数据
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<UpdateDoubleVO> batchUpdateIncomeExpenditureDataLong2(List<UpdateDoubleDTO> updateList) {
        if (isEmpty(updateList)) {
            throw new RuntimeException("待处理数据列表不能为空");
        }
        Boolean isUpdate = IECommUtil.isUpdateButton(); //true可以修改
        if (!isUpdate) {
            throw new RuntimeException("当前用户角色无修改收支数据的权限");
        }

        // 分组
        Map<Boolean, List<UpdateDoubleDTO>> groupedData = updateList.stream()
                .collect(Collectors.partitioningBy(dto -> !isBlank(dto.getId())));
        List<UpdateDoubleDTO> updateDTOList = groupedData.get(Boolean.TRUE); // 修改数据
        List<UpdateDoubleDTO> insertDTOList = groupedData.get(Boolean.FALSE); // 新增数据

        // 修改逻辑
        List<IncomeExpenditure> updateEntities = handleUpdateData(updateDTOList);

        // 新增逻辑
        List<IncomeExpenditure> insertEntities = handleInsertData(insertDTOList);

        // 批量操作
        executeBatchOperations(updateEntities, insertEntities);

        // 构建并返回结果
        return buildResult(updateEntities, insertEntities);
    }

// ------------------------------ 核心处理方法 ------------------------------
    /**
     * 处理修改数据逻辑
     */
    private List<IncomeExpenditure> handleUpdateData(List<UpdateDoubleDTO> updateDTOList) {
        if (isEmpty(updateDTOList)) {
            return new ArrayList<>();
        }

        // 提取修改ID并查询原始数据
        List<String> updateIds = updateDTOList.stream()
                .map(UpdateDoubleDTO::getId)
                .collect(Collectors.toList());
        List<IncomeExpenditure> originalEntities = this.list(new LambdaQueryWrapper<IncomeExpenditure>()
                .in(IncomeExpenditure::getId, updateIds)
                .eq(IncomeExpenditure::getDeleted, false));

        // 校验原始数据存在性
        validateOriginalDataExists(updateIds, originalEntities);

        // 构建原始数据ID映射
        Map<String, IncomeExpenditure> originalMap = originalEntities.stream()
                .collect(Collectors.toMap(IncomeExpenditure::getId, Function.identity()));

        // 校验修改合法性 + 构建修改实体
        return updateDTOList.stream()
                .map(dto -> {
                    IncomeExpenditure original = originalMap.get(dto.getId());
                    validateUpdateLegality(dto, original); // 校验code和year（仅code和year，distinct就不要前端传了，有前缀+中文）

                    // 建造者模式：一行完成实体构建，替代所有set方法
                    return IncomeExpenditureBuilder.anIncomeExpenditure()
                            .fromOriginal(original) // 复制原始数据
                            .withMoneyFields(moneyToDB(dto)) // 仅覆盖允许修改的金额字段
                            .withUpdateInfo(SecurityPlusUtils.getCurrentUsername()) // 自动设置更新信息
                            .build(); // 自动计算合计字段
                })
                .collect(Collectors.toList());
    }

    /**
     * 处理新增数据逻辑
     */
    private List<IncomeExpenditure> handleInsertData(List<UpdateDoubleDTO> insertDTOList) {
        if (isEmpty(insertDTOList)) {
            return new ArrayList<>();
        }

        int currentYear = LocalDate.now().getYear();

        // 校验数据有么有，year<=当前年
        insertDTOList.forEach(dto -> {
            validateInsertRequiredFields(dto);
            validateYearLegality(dto.getYear(), currentYear);
            validateInsertUniqueness(dto); // 仅校验code+year唯一
        });

        // 构建实体
        return insertDTOList.stream()
                .map(dto -> {
                    // 查询关联的城市信息（code是前端必传的）
                    CommCity commCity = getCommCity(dto.getCode());

                    // 【注意】这里的逻辑，如果前端传的district包含"-"，则取code对应的城市名称，因为区级前面加了xx市-
                    if(dto.getDistrict().contains("-")){
                        dto.setDistrict(commCity.getName());
                    }

                    // 填充值
                    return IncomeExpenditureBuilder.anIncomeExpenditure()
                            .withId(IDWork.getSequenceId2String())
                            .withCode(commCity.getCode())
                            .withDistrict(dto.getDistrict())
                            .withYear(Integer.parseInt(dto.getYear()))
                            .withLevelCode(commCity.getLevelCode())
                            .withMergerName(commCity.getMergerName())
                            .withParentCode(commCity.getParentCode())
                            .withMoneyFields(moneyToDB(dto))
                            .withCreateInfo(SecurityPlusUtils.getCurrentUsername())
                            .build();
                })
                .collect(Collectors.toList());
    }

// ------------------------------ 辅助方法 ------------------------------

    /**
     * 执行批量操作（修改+新增）
     */
    private void executeBatchOperations(List<IncomeExpenditure> updateEntities, List<IncomeExpenditure> insertEntities) {
        if (!isEmpty(updateEntities)) {
            this.updateBatchById(updateEntities); // 批量修改
        }
        if (!isEmpty(insertEntities)) {
            this.saveBatch(insertEntities); // 批量新增
        }
    }

    /**
     * 构建返回结果
     */
    private List<UpdateDoubleVO> buildResult(List<IncomeExpenditure> updateEntities, List<IncomeExpenditure> insertEntities) {
        // 合并所有操作的ID
        List<String> allIds = Stream.concat(
                updateEntities.stream().map(IncomeExpenditure::getId),
                insertEntities.stream().map(IncomeExpenditure::getId)
        ).collect(Collectors.toList());

        // 查询最新数据并转换为VO返回
        return this.listByIds(allIds).stream()
                .map(this::iEToVO)
                .collect(Collectors.toList());
    }

// ------------------------------ 校验方法 ------------------------------

    /**
     * 校验原始数据是否存在（修改）
     */
    private void validateOriginalDataExists(List<String> updateIds, List<IncomeExpenditure> originalEntities) {
        if (originalEntities.size() != updateIds.size()) {
            List<String> existIds = originalEntities.stream()
                    .map(IncomeExpenditure::getId)
                    .collect(Collectors.toList());
            List<String> invalidIds = updateIds.stream()
                    .filter(id -> !existIds.contains(id))
                    .collect(Collectors.toList());
            throw new RuntimeException("ID为" + String.join(",", invalidIds) + "的数据不存在或已删除");
        }
    }

    /**
     * 校验修改合法性
     */
    private void validateUpdateLegality(UpdateDoubleDTO dto, IncomeExpenditure original) {
        // 年份不同不可修改
        if (StringUtils.isNotBlank(dto.getYear())) {
            Integer newYear = Integer.valueOf(dto.getYear());
            if (!newYear.equals(original.getYear())) {
                throw new RuntimeException("ID为" + dto.getId() + "的数据禁止修改年份（原始年份：" + original.getYear() + "）");
            }
        }

        // code不同不能修改
        if (StringUtils.isNotBlank(dto.getCode()) && !dto.getCode().equals(original.getCode())) {
            throw new RuntimeException("ID为" + dto.getId() + "的数据code不匹配（原始code：" + original.getCode() + "）");
        }
    }

    /**
     * 校验新增数据必填字段 code+year
     */
    private void validateInsertRequiredFields(UpdateDoubleDTO dto) {
        if (StringUtils.isBlank(dto.getCode())) {
            throw new RuntimeException("新增数据的code不能为空");
        }
        if (StringUtils.isBlank(dto.getYear())) {
            throw new RuntimeException("新增数据的year不能为空");
        }
    }

    /**
     * 校验新增年份合法性：前端那边传进来的year必须小于今年
     */
    private void validateYearLegality(String yearStr, int currentYear) {
        try {
            int inputYear = Integer.parseInt(yearStr);
            if (inputYear > currentYear) {
                throw new RuntimeException("新增失败：年份" + inputYear + "不能大于当前年" + currentYear);
            }
        } catch (NumberFormatException e) {
            throw new RuntimeException("新增失败：年份" + yearStr + "格式错误，必须为数字（如2024）");
        }
    }

    /**
     * 校验新增数据唯一性（仅code+year组合不能重复，因为这个是必传字段）
     */
    private void validateInsertUniqueness(UpdateDoubleDTO dto) {
        LambdaQueryWrapper<IncomeExpenditure> existQuery = new LambdaQueryWrapper<>();
        existQuery.eq(IncomeExpenditure::getCode, dto.getCode())
                .eq(IncomeExpenditure::getYear, Integer.parseInt(dto.getYear()))
                .eq(IncomeExpenditure::getDeleted, false);  // 排除已删除数据

        long count = this.count(existQuery);
        if (count > 0) {
            throw new RuntimeException(
                    "新增失败：code=" + dto.getCode() +
                            "、year=" + dto.getYear() + "的数据已存在"
            );
        }
    }

    /**
     * 查询comm_city表并校验数据存在与否
     */
    private CommCity getCommCity(String code) {
        LambdaQueryWrapper<CommCity> cityQueryWrapper = new LambdaQueryWrapper<>();
        cityQueryWrapper.eq(CommCity::getCode, code)
                .eq(CommCity::getDeleted, "0")  // 假设0表示未删除
                .select(
                        CommCity::getCode,
                        CommCity::getLevelCode,
                        CommCity::getMergerName,
                        CommCity::getParentCode,
                        CommCity::getName
                );

        CommCity commCity = commCityMapper.selectOne(cityQueryWrapper);
        if (commCity == null) {
            throw new RuntimeException(
                    "新增失败：未找到code=" + code + "的城市信息"
            );
        }
        return commCity;
    }


    private UpdateDoubleVO moneyToVO(IncomeExpenditure entity) {
        UpdateDoubleVO updateDoubleVO = new UpdateDoubleVO();
        if (entity == null)
            return updateDoubleVO;
        // 经费来源字段：Long → Double（万元单位，保留对应小数位数）
        if (entity.getCityAllocated() != null) {
            updateDoubleVO.setCityAllocated(IECommUtil.longToDouble(entity.getCityAllocated()));
        }
        if (entity.getDistrictAllocated() != null) {
            updateDoubleVO.setDistrictAllocated(IECommUtil.longToDouble(entity.getDistrictAllocated()));
        }
        if (entity.getOtherFunds() != null) {
            updateDoubleVO.setOtherFunds(IECommUtil.longToDouble(entity.getOtherFunds()));
        }
        if (entity.getTotalFunds() != null) {
            updateDoubleVO.setTotalFunds(IECommUtil.longToDouble(entity.getTotalFunds()));
        }

        // 已支付字段：Long → Double（万元单位）
        if (entity.getPurchaseMaintain() != null) {
            updateDoubleVO.setPurchaseMaintain(IECommUtil.longToDouble(entity.getPurchaseMaintain()));
        }
        if (entity.getPurchaseEquipment() != null) {
            updateDoubleVO.setPurchaseEquipment(IECommUtil.longToDouble(entity.getPurchaseEquipment()));
        }
        if (entity.getDiseasePrevention() != null) {
            updateDoubleVO.setDiseasePrevention(IECommUtil.longToDouble(entity.getDiseasePrevention()));
        }
        if (entity.getExpertTraining() != null) {
            updateDoubleVO.setExpertTraining(IECommUtil.longToDouble(entity.getExpertTraining()));
        }
        if (entity.getThirdPartyService() != null) {
            updateDoubleVO.setThirdPartyService(IECommUtil.longToDouble(entity.getThirdPartyService()));
        }
        if (entity.getOtherPayment() != null) {
            updateDoubleVO.setOtherPayment(IECommUtil.longToDouble(entity.getOtherPayment()));
        }
        if (entity.getTotalPayment() != null) {
            updateDoubleVO.setTotalPayment(IECommUtil.longToDouble(entity.getTotalPayment()));
        }

        // 被收回资金字段：Long → Double（万元单位）
        if (entity.getCityRecovered() != null) {
            updateDoubleVO.setCityRecovered(IECommUtil.longToDouble(entity.getCityRecovered()));
        }
        if (entity.getDistrictRecovered() != null) {
            updateDoubleVO.setDistrictRecovered(IECommUtil.longToDouble(entity.getDistrictRecovered()));
        }
        return updateDoubleVO;
    }

    private UpdateDoubleVO iEToVO(IncomeExpenditure entity) {
        UpdateDoubleVO updateDoubleVO = new UpdateDoubleVO();
        if (entity == null)
            return updateDoubleVO;

        //转换非金额部分
        updateDoubleVO.setId(entity.getId())
                .setCode(entity.getCode())
                .setDistrict(entity.getDistrict())
                .setYear(entity.getYear());

        //转换金额部分
        UpdateDoubleVO moneyToVO = moneyToVO(entity);
        BeanCopyUtil.copyPropertiesIgnoreNull(moneyToVO, updateDoubleVO);
        return updateDoubleVO;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<IncomeExpenditureDetailVO> updateOrInsertIE(List<UpdateOrInsertIEDTO> dataList) {

        return Collections.emptyList();
    }

    @Override
    public List<IncomeExpenditure> getAllData() {
        return this.list();
    }

    @Override
    public List<CodeNameVO> getDistricts() {
        DataScope scope = this.getDataScopeByIE(); // 460000
        String area = scope.getArea();
        Integer roleLevel = SecurityPlusUtils.getMinLevel(); //roleLevel:1
        List<String> oldDistrictCodes = this.listByDataScopeArea(scope, roleLevel); //管辖区域
        //完整管辖区
        List<String> districtCodes = oldDistrictCodes.stream().map(code -> {
            return area + "," + code;
        }).collect(Collectors.toList());

        System.out.println("area:" + area);
        System.out.println("oldDistrictCodes:" + oldDistrictCodes);
        System.out.println("districtCodes:" + districtCodes);

        List<CodeNameVO> codeNameVOS = commCityMapper.selectCodeAndNameByCodes(districtCodes);
        return codeNameVOS;
    }

}